package com.woniu.utils;

import com.woniu.pojo.User;
import com.woniu.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

//AuthenticatingRealm父类只负责用户认证（登录）
//AuthorizingRealm父类既支持登录也支持权限分配
public class UaccountRealm extends AuthorizingRealm {
    @Resource
    private UserService userService;
    //用户认证的方法，不能手动调用，Shiro自动调用
    //用户身份 这里存放着用户的账号和密码
    //如果认证失败shiro会抛出各种异常
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken= (UsernamePasswordToken) token;
        String username=usernamePasswordToken.getUsername();//获取页面中的用户账号
        String password=new String(usernamePasswordToken.getPassword());//获取页面中的用户密码实际工作中基本不需要获取
        //认证账号，这里应该从数据库获取数据，如果进入if表示账号不存在要抛出异常
        //判断当前账号是否可用 Ip是否允许等等，根据不同的逻辑可以抛出不同的异常
        int i=0;
        User user = userService.findOneByUaccount(username);
        if (user!=null){
            if (password.equals(user.getUpwd())){
                if (user.getSoftdel().equals("0")){
                    throw new LockedAccountException();
                }
            }else{
                throw new IncorrectCredentialsException();
            }
        }else{
            throw new UnknownAccountException();
        }

        SecurityUtils.getSubject().getSession().setAttribute("loginUser",user);
        //设置让当前登录用户中的密码进行数据加密
         /*
        数据密码加密主要是防止数据再浏览器到后台服务器之间的数据传递时被篡改或者窃取，因此应该在前台到后台的过程中
        进行加密，而我们这里的加密其实是将浏览器中获取后台的密码加密和对数据库中的数据进行加密
        这就丢失了数据加密的意义因此不建议在这里进行加密，应该在页面传递时进行加密
        HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(2);
        this.setCredentialsMatcher(credentialsMatcher);
        //对数据库中的密码进行加密
        Object obj=new SimpleHash("MD5","123456","",2);
         */

        //创建密码认证对象，由Shiro自动认证密码
        //参数1 为数据库中的账号
        //参数2 为数据库中读取来的密码
        //参数3 为当然Realm的名字
        //如果密码认证成功则返回一个用户身份对象。如果认证失败会抛出异常IncorrectCredentialsException
        return new SimpleAuthenticationInfo(user,password,getName());
    }

    public static void main(String[] args) {
        //使用shiro提供的工具类对数据进行加密
        //参数1 为加密算法名，我们使用MD5 这是一个不可逆的加密算法
        //参数2 为需要加密的数据
        //参数3 加密的盐值 用于改变加密结果的 不同的盐加密的数据是不一致的
        //参数4 加密的次数
        Object obj=new SimpleHash("MD5","123456","",1);
        System.out.println(obj);
        Object obj1=new SimpleHash("MD5","123456","admin",1);
        Object obj2=new SimpleHash("MD5","123456","admin1",1);
        System.out.println("使用盐admin加密"+obj1);
        System.out.println("使用盐admin1加密"+obj2);
    }
    //用户授权的方法,当用户认证通过以后每次访问需要授权的请求时都需要执行这段代码来完善授权操作
    //这里应该查询数据库来获取当前用户的所有角色和权限，并设置到Shiro中
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("-------------授权了--------------");
        User user=(User)(principals.getPrimaryPrincipal());
        //获取用户账号，根据账号来从数据库中获取数//定义用户角色的set集合这个集合应该来自数据库
        /*
        注意：由于每次点击需要授权的请求时，Shiro都会执行这个方法，因此如果这里的数据是来自于数据库中的
        那么一定要控制好不能每次都从数据库中读取数据，那么这样效率太低了
        Set<String> roles=new HashSet<>();
        设置角色，这里的操作应该是从数据库中读取数据
         */
        Set<String> roles=userService.findUserRoles(user.getUid());
        System.out.println(roles);
        //设置权限，这里的操作应该是从数据库中读取数据
        Set<String> permissions=userService.findUserPermission(user.getUid());
        System.out.println(permissions);
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        info.setRoles(roles);//设置角色信息
        info.setStringPermissions(permissions);
        return info;
    }
}
